iT邦幫忙

2024 iThome 鐵人賽

DAY 7
0

本次目標

  1. 了解 Graceful shutdown
  2. Graceful shutdown 好處
  3. preStop hook
  4. 長時間任務運行問題

了解 Graceful shutdown

Graceful shutdown 可以稱優雅關閉,是指應用程式在接收到 SIGTERM 訊號後,能有有條理的結束應用程式運作,而不是直接被強制終止。這讓應用程式可以在關閉前完成正在進行的工作,保存重要的狀態資訊,並釋放佔用的系統資源等。

為什麼 graceful shutdown 這麼重要?

  1. 可以避免資料遺漏,導致不一致性
  2. 安全管理應用程式,減少因異常關閉導致的系統問題,提高系統的整體穩定性

回到 Kubernetes 上,想一下刪除 Pod 時會發生什麼事 ? 如果還記得上一篇所提到的話,Endpoint 物件會移除該 Pod IP。也會觸發 kube-proxy、Ingress controller、DNS、service mesh 等所有事件,這些動作將會停止流量被路由到要被停止的 IP。

這過程元件並非即時的操作,因此無法保證需要多長時間才能將 IP 從其內部狀態中刪除。Pod 狀態也會被更新成 Terminate。而 kubelet 將會進行

  • 卸載 volume (CSI)
  • 釋放 IP (CNI)
  • 移除 container (CRI)

如果從 kube-proxyIngress controller 中刪除端點(Pod IP)之前終止 Pod,將可能會遇到停機。即 Kubernetes 仍將流量路由到該 IP,但 Pod 已不存在。

要避免需透過等待,當 Pod 即將刪除時,它會收到 SIGTERM 訊號。此時應用程式可以捕獲該訊號並開始關閉。

端點不太可能立即從 Kubernetes 中的所有元件(Ingress controller, kube-proxy, CoreDNS etc.)中即時刪除。因此可以

  • 被移除前等待一段時間
  • 儘管有 SIGTERM,仍處理傳入流量
  • 最後,關閉現有的長期連線(long-lived connections)
    • 資料庫連線
    • WebSockets
  • 終止該進程

預設情況下,Kubernetes 將發送 SIGTERM 訊號並等待 30 秒(Pod.spec.terminationGracePeriodSeconds),然後再強制終止進程。這 30 秒過程,前幾秒會傳播端點移除的訊息給 kube-proxyIngress controller 等資源。這過程還會持續接收流量只不過是越來越少,直到沒有,當沒有流量時,可以安全關閉與資料庫的連線或任何持久連線並終止進程。

如果 30 秒內其中 15 秒無法把元件關聯的端點移除則時間就會被往後延長。當到 30 秒後,Pod 將會被強制移除,30 秒為 Pod.spec.terminationGracePeriodSeconds 預設是可以異動。

如果舊有架構無法更改程式碼來接收 SIGTERM 並等待更長時間怎麼辦?可以使用腳本方式睡眠等待固定的時間,然後讓應用程式退出。在 Pod 中可以使用 preStop 這個關鍵欄位。

preStop hook 應用

preStop hook 是 Pod LifeCycle Hook 之一。在執行 SIGTERM 之前,Kubernetes 在 Pod 中有 preStop hook,可以設定 15 秒等待

# 官方範例
apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      preStop:
        exec:
          command: ["sleep","15"]

在應用程式中使用 preStop hook 和等待 15 秒是不同的。在將 SIGTERM 分派到應用程式之前呼叫 preStop hook,因此 preStop 運行時,應用程式並不知道它即將關閉。另一方面 preStop hook 中的延遲是 terminationGracePeriodSeconds 中 30 秒的一部分。

如果 preStop hook 等待 25 秒,一旦完成,容器收到 SIGTERM。它剩下 5 秒的時間終止,否則 kubelet 將發出 SIGKILL。另一方面是 preStop hook 大於 terminationGracePeriodSecondskubelet 將發送 SIGKILL 訊號並強制終止容器,過程中不會調度 SIGTERM 訊號。如果需要更長延遲調整 terminationGracePeriodSeconds 是對的。

preHook 的設定會取決於,如果應用程式中正常處理關閉,時間可能會要 60 秒甚至更短,相反如果應用程式必須在關閉前刷新日誌或指標,則可能需要更長的時間間隔。下圖為一個執行時序圖


From https://learnk8s.io/graceful-shutdown#how-to-gracefully-shut-down-pods

對於長時間執行的任務

如果應用程式提供長期連線(WebSocket),不希望在 30 秒內終止它。理想上是用戶端終止,讓連線正常中斷。同樣,如果要對大型影片進行轉碼,不希望滾動更新時刪除當前的 Pod(以及工作時間)。

如何避免延遲關閉 Pod?

加大 terminationGracePeriodSeconds 時間,希望能夠連線被消費完或作業完成。但存在一些缺點

  • 如果依賴 Prometheus 來抓取指標,將無法收集指標。因端點被移除
  • 由於正在運行和終止的 Pod 可能位於不同的版本,因此除錯更具挑戰性
  • kubelet 不會檢查 liveness probe。例如,如果進程卡住了,它不會重新啟動它

應該考慮為每個版本建立新的部署,而不是增加時間。當建立全新的 Deployment 時,現有的 Deployment 保持不變,同時長時間運行的作業可以像往常一樣繼續處理,並且長期連接也保持不變。

部分內容是翻譯至參考[2],工作上確實有發生此問題因此有特別留上述筆記向上報告。

實務上在整合 celery worker 時,確實存在問題。當沒特別設定 terminationGracePeriodSeconds 時如果 Pod 被縮減,就發生任務卡住且無法復原情況,因此為了短暫避免此問題發生,就加大了時間至 3600 秒。但這理論上不是最佳解,反而是要往 Job 方向去思考是否能解決這件事。

下個章節將透過 Quarkus 實際實現。

  1. Kubernetes | pod-lifecycle
  2. learnk8s | graceful-shutdown

上一篇
為 Quarkus 實作 Kubernetes Pod 健康檢查
下一篇
Quarkus 最後一哩路
系列文
當 Quarkus 想要騎乘駱駝並用8腳章魚掌控舵手 31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言